﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DHQGHN.DAL.DataMapper
{
    class GenericMapper : IDataMapper
    {
        public System.Type DtoType { get; set; }
        private bool _isInitialized = false;
        private List<PropertyOrdinalMap> PropertyOrdinalMappings;
        /// <summary>
        /// Generic Mapper
        /// </summary>
        /// <param name="type"></param>
        public GenericMapper(System.Type type)
        {
            DtoType = type;
        }
        /// <summary>
        /// Initialize Mapper
        /// </summary>
        /// <param name="reader"></param>
        private void InitializeMapper(IDataReader reader)
        {
            //PopulatePropertyOrdinalMappings(reader);
            PopulatePropertyOrdinalMappingsCustom(reader);
            _isInitialized = true;
        }
        /// <summary>
        /// Populate Property Ordinal Mappings
        /// </summary>
        /// <param name="reader"></param>
        public void PopulatePropertyOrdinalMappings(IDataReader reader)
        {
            // Get the PropertyInfo objects for our DTO type and map them to 
            // the ordinals for the fields with the same names in our reader.  
            PropertyOrdinalMappings = new List<PropertyOrdinalMap>();
            PropertyInfo[] properties = DtoType.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                PropertyOrdinalMap map = new PropertyOrdinalMap();
                map.Property = property;
                try
                {
                    map.Ordinal = reader.GetOrdinal(property.Name);
                    PropertyOrdinalMappings.Add(map);
                }
                catch { }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="reader"></param>
        public void PopulatePropertyOrdinalMappingsCustom(IDataReader reader)
        {
            // Get the PropertyInfo objects for our DTO type and map them to 
            // the ordinals for the fields with the same names in our reader.  
            if (Map_Dictionary.Count > 0)
            {
                PropertyOrdinalMappings = new List<PropertyOrdinalMap>();
                PropertyInfo[] properties = DtoType.GetProperties();

                foreach (PropertyInfo property in properties)
                {
                    PropertyOrdinalMap map = new PropertyOrdinalMap();
                    map.Property = property;

                    foreach (KeyValuePair<string, string> pair in Map_Dictionary)
                    {
                        try
                        {
                            if (property.Name.ToUpper() == pair.Key.ToUpper())
                            {
                                map.Ordinal = reader.GetOrdinal(pair.Value);
                                PropertyOrdinalMappings.Add(map);
                                break;
                            }
                        }
                        catch 
                        {
                            break;
                        }

                        //Console.WriteLine("{0}, {1}",
                        //pair.Key,
                        //pair.Value);
                    }
                }
            }
        }
        /// <summary>
        /// GetData
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public Object GetData(IDataReader reader)
        {
            if (!_isInitialized) 
            { 
                InitializeMapper(reader); 
            }

            object dto = Activator.CreateInstance(DtoType);
            foreach (PropertyOrdinalMap map in PropertyOrdinalMappings)
            {
                try
                {
                    if (!reader.IsDBNull(map.Ordinal))
                    {
                        string sGetDataTypeName = reader.GetDataTypeName(map.Ordinal);
                        string sGetFieldType = reader.GetFieldType(map.Ordinal).ToString();
                        string sGetName = reader.GetName(map.Ordinal).ToString();

                        //map.Property.SetValue(dto, reader.GetValue(map.Ordinal), null);
                        switch (map.Property.PropertyType.FullName)
                        {
                            case "System.String":
                                //nvarchar ==> SqlString
                                if (sGetFieldType == "System.Int16")
                                {
                                    map.Property.SetValue(dto, reader.GetInt16(map.Ordinal).ToString(), null);
                                }
                                else if (sGetFieldType == "System.Int32")
                                {
                                    map.Property.SetValue(dto, reader.GetInt32(map.Ordinal).ToString(), null);
                                }
                                else if (sGetFieldType == "System.Int64")
                                {
                                    map.Property.SetValue(dto, reader.GetInt64(map.Ordinal).ToString(), null);
                                }
                                else
                                {
                                    map.Property.SetValue(dto, reader.GetString(map.Ordinal), null);
                                }
                                break;
                            case "System.DateTime": //datetime/date ==> SqlDateTime
                                map.Property.SetValue(dto, reader.GetDateTime(map.Ordinal), null);
                                break;
                            case "System.Int32": //int ==> SqlInt32
                                map.Property.SetValue(dto, reader.GetInt32(map.Ordinal), null);
                                break;
                            case "System.Int64": //bigint ==> SqlInt64
                                map.Property.SetValue(dto, reader.GetInt64(map.Ordinal), null);
                                break;
                            case "System.Int16":
                                //smallint ==> SqlInt16
                                map.Property.SetValue(dto, reader.GetInt16(map.Ordinal), null);
                                break;
                            case "System.Decimal":
                                //decimal/money/numeric ==> SqlDecimal/SqlMoney/SqlDecimal
                                //map.Property.SetValue(dto, reader.GetBoolean(map.Ordinal), null); //vanduong
                                map.Property.SetValue(dto, reader.GetDecimal(map.Ordinal), null); //vanduong
                                break;
                            case "System.Boolean": //bit ==> SqlBoolean
                                if (sGetFieldType == "System.String")
                                {
                                    if (reader.GetString(map.Ordinal).ToUpper() == "YES")
                                    {
                                        map.Property.SetValue(dto, true, null);
                                    }
                                    else
                                    {
                                        map.Property.SetValue(dto, false, null);
                                    }
                                }
                                else //vanduong
                                {
                                    map.Property.SetValue(dto, reader.GetBoolean(map.Ordinal), null);
                                }
                                break;
                            case "System.Double": //float ==> SqlDouble
                                map.Property.SetValue(dto, reader.GetDouble(map.Ordinal), null);
                                break;
                            case "System.Guid":
                                //uniqueidentifier ==> SqlGuid
                                map.Property.SetValue(dto, reader.GetGuid(map.Ordinal), null);
                                break;
                            case "System.Byte":
                                // tinyint ==> SqlByte
                                map.Property.SetValue(dto, reader.GetByte(map.Ordinal), null);
                                break;
                            default:
                                map.Property.SetValue(dto, reader.GetValue(map.Ordinal), null);
                                break;
                        }
                    }
                }
                catch { }
            }

            return dto;
        }
        /// <summary>
        /// Property Ordinal Map
        /// </summary>
        private class PropertyOrdinalMap
        {
            public PropertyInfo Property { get; set; }
            public int Ordinal { get; set; }
        }
        /// <summary>
        /// Get Record Count
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public int GetRecordCount(IDataReader reader)
        {
            Object count = reader["RECODE_COUNT"];
            return count == null ? 0 : Convert.ToInt32(count);
        }

        public string[] Field_List {get;set;}

        public Type[] Field_Type_List {get;set;}

        public string[] Param_List {get;set;}

        public Type ModelType {get;set;}

        public Dictionary<string, string> Map_Dictionary{get;set;}
    }
}
